package ex1;

public class Cell {
	private boolean done; // turns on when generation reached the max gens
	private boolean alive; // live or dead
	private boolean dead4good;
	private boolean sick; // sick or not
	private int generation;
	private int max_gen; // max generation to reach
	private int count; // living neighbors counter
	// private boolean sickInPrev;
	private Integer[] currentNeighbors; // neighbors
	// statuses
	private Integer[] futureNeighbors; // next
	// statuses
	// of
	// neighbors
	private boolean[] border; // up, down,

	// left, right

	// indicates if the cell touches cells belongs other workers

	public Cell(boolean alive, int max_gen) { // c'tor
		
		this.alive = alive;
		this.done = false;
		this.dead4good = false;
		this.sick = false;
		this.generation = 0;
		this.max_gen = max_gen;
		this.count = -1;
		// this.sickInPrev = false;
		this.border = new boolean[4];
		for (int i = 0; i < border.length; i++)
			border[i] = false;
		currentNeighbors = new Integer[8];
		futureNeighbors = new Integer[8];

		for (int i = 0; i < currentNeighbors.length; i++) {
			currentNeighbors[i] = new Integer(-1);
			futureNeighbors[i] = new Integer(-1);
		}
	}

	// calculates my index from his point of view
	public static int indexTranslation(int index) {
		return 7 - index;
	}

	public boolean isDone() {
		return this.done;
	}

	public boolean isAlive() {
		return this.alive;
	}

	public void setStatus(boolean status) {
		this.alive = status;
	}

	public boolean isSick() {
		return (this.sick);
	}

	public int getGeneration() {
		return this.generation;
	}

	public boolean isBorder() {
		boolean status = false;
		for (int i = 0; i < border.length; i++)
			status = status || border[i];

		return status;
	}

	public void setBorder(int index, boolean borderStatus) {
		this.border[index] = borderStatus;
	}

	private void updateStatus() { // update the status according the
		// rules
		boolean result = false;
		if (dead4good)
			return;
		if (count == 3 || (alive && count == 2)) {
			result = true;
			sick = false;
		}

		if (count == 4) {
			if (sick) {
				// Sick for the second time, dies for good
				dead4good = true;
				sick = false;
			} else {
				if (alive) {
					sick = true;
					result = true;
				}
			}
		} else {
			sick = false;
		}

		alive = result;

	}

	// set status of neighbor in given index to 'isAlive'
	// !!!!!! synchronized !!!!!!!
	public void setNeighbour(int index, boolean isAlive) {
		int result = isAlive ? 1 : 0;
		boolean[] arr = neighborsInOtherWorkers();
		if (arr[index]) { // the neighbor belongs to other worker
			synchronized (this.futureNeighbors[index]) {
				synchronized (this.currentNeighbors[index]) {
					setSpecificNeighbor(index, result);
				}
			}
		} else { // in the same worker
			setSpecificNeighbor(index, result);
		}
	}

	private void setSpecificNeighbor(int index, int result) {
		if (currentNeighbors[index].equals(-1))
			currentNeighbors[index] = new Integer(result);
		else if (futureNeighbors[index].equals(-1))
			futureNeighbors[index] = new Integer(result);
	}

	public int getNeighbour(int index) {
		return currentNeighbors[index];
	}

	// indicates which neighbors belong to other workers
	public boolean[] neighborsInOtherWorkers() {
		boolean[] neighbors = new boolean[8];
		for (int i = 0; i < neighbors.length; i++)
			neighbors[i] = false;

		if (border[0]) { // up
			neighbors[0] = true;
			neighbors[1] = true;
			neighbors[2] = true;
		}
		if (border[1]) { // down
			neighbors[5] = true;
			neighbors[6] = true;
			neighbors[7] = true;
		}
		if (border[2]) { // left

			neighbors[0] = true;
			neighbors[3] = true;
			neighbors[5] = true;
		}
		if (border[3]) { // right

			neighbors[2] = true;
			neighbors[4] = true;
			neighbors[7] = true;
		}
		return neighbors;
	}

	// is ready to advance? did we receive information from all neighbors
	public boolean isReady() {

		boolean[] neighbors = neighborsInOtherWorkers();
		for (int i = 0; i < neighbors.length; i++)
			if (!neighbors[i])
				if (currentNeighbors[i] == -1) {
					return false;
				}

		for (int i = 0; i < neighbors.length; i++)
			if (neighbors[i])
				synchronized (this.currentNeighbors[i]) {
					if (currentNeighbors[i] == -1) {
						return false;
					}
				}
		
		count++;
		
		for (int i = 0; i < neighbors.length; i++) {
			count += currentNeighbors[i];

		}
		
		return true;
	}

	public void updateCounter() { // advance the counter one generation

		boolean[] neighbors = neighborsInOtherWorkers();
		for (int i = 0; i < futureNeighbors.length; i++) {
			if (neighbors[i]) {
				synchronized (this.futureNeighbors[i]) {
					synchronized (this.currentNeighbors[i]) {
						updateNeighbors(i);
					}
				}
			} else {
				updateNeighbors(i);
			}
		}
		count = -1;
	}

	private void updateNeighbors(int i) {
		int n = futureNeighbors[i];
		currentNeighbors[i] = new Integer(n);
		futureNeighbors[i] = new Integer(-1);
	}

	// advance to next generation
	public void nextGeneration() {
		// assume: the counter is already checked and we surely can advance

		updateStatus();
		generation++;
		updateCounter();

		if (generation == max_gen)
			done = true;

	}
}
